from   flask   import Flask , request , session , g , redirect , \
                      url_for , abort , render_template , flash
import os
import sqlite3


app = Flask(__name__)            # create the application instance :)
app.config.from_object(__name__) # load config from this file , myapp.py

#execfile(app.root_path + '/constants/app_constants.py')         # python2
exec(open(app.root_path + '/constants/app_constants.py').read()) # python3

# Load default config and override config from an environment variable
app.config.update(dict(DATABASE   = os.path.join(app.root_path , DB_FILE) ,
                       SECRET_KEY = APP_SECRET                            ,
                       USERNAME   = ADMIN_LOGIN                           ,
                       PASSWORD   = ADMIN_PASS                            ))
app.config.from_envvar(SETTINGS_ENV_VAR , silent = True)


## helpers ##

def connect_db():
  db             = sqlite3.connect(app.config[DB_KEY])
  db.row_factory = sqlite3.Row

  return db


def get_db():
  if not hasattr(g , DB_ATTR):
    g.sqlite_db = connect_db()

  return g.sqlite_db


def init_db():
  db = get_db()
  with app.open_resource(DB_SCHEMA_FILE , mode = 'r') as sql_file:
    db.cursor().executescript(sql_file.read())
  db.commit()


def count_posts():
  db     = get_db()
  cursor = db.execute(COUNT_POSTS_SQL)
  result = cursor.fetchone()

  return result[0]


def insert_post(params):
  db = get_db()
  db.execute(CREATE_POST_SQL , [ params[TITLE_KEY] , params[MESSAGE_KEY] ])
  db.commit()


@app.cli.command(INIT_DB_CMD)
def initdb_command():
  init_db()
  print(INIT_DB_MSG)


@app.teardown_appcontext
def close_db(error):
  if hasattr(g , DB_ATTR):
    g.sqlite_db.close()


def is_signed_in(): return session.get(IS_AUTHED_KEY)


## routes ##

@app.route('/*')
def catchall(): abort(NOTFOUND_STATUS)


@app.route(ROOT_ROUTE)
def web_root(): return redirect(LOGIN_ROUTE)


@app.route(LOGIN_ROUTE , methods = [GET_METHOD , POST_METHOD])
def login():
  error = None
  if request.method == POST_METHOD:
    if   request.form[USERNAME_NAME] != app.config[USERNAME_KEY]: error = LOGIN_ERROR_TEXT
    elif request.form[PASSWORD_NAME] != app.config[PASSWORD_KEY]: error = PASS_ERROR_TEXT
    else:
      session[IS_AUTHED_KEY] = True
      flash(LOGIN_TEXT)

      return redirect(url_for(INDEX_POSTS_ACTION))

  return render_template(LOGIN_HTML , error = error)


@app.route(LOGOUT_ROUTE)
def logout():
  session.pop(IS_AUTHED_KEY , None)
  flash(LOGOUT_TEXT)

  return redirect(url_for(INDEX_POSTS_ACTION))


@app.route(INDEX_POSTS_ROUTE)
def index_posts():
  db     = get_db()
  cursor = db.execute(FETCH_POSTS_SQL)
  result = cursor.fetchall()

  return render_template(INDEX_POSTS_HTML , posts = result , header_text = INDEX_POSTS_TEXT.decode())


@app.route(SHOW_POST_ROUTE)
def show_post(resource):
  db     = get_db()
  cursor = db.execute(FETCH_POST_SQL , [resource])
  result = cursor.fetchone()

  if result == None: abort(NOTFOUND_STATUS)

  return render_template(SHOW_POST_HTML , post = result , header_text = SHOW_POST_TEXT.decode())


@app.route(NEW_POST_ROUTE)
def new_post():
  if not is_signed_in(): return redirect(LOGIN_ROUTE)
  else:                  return render_template(NEW_POST_HTML , post = dict())


@app.route(CREATE_POST_ROUTE , methods = [POST_METHOD])
def create_post():
  if not is_signed_in(): abort(UNAUTH_STATUS)

  params              = dict()
  params[TITLE_KEY  ] = request.form[TITLE_NAME  ]
  params[MESSAGE_KEY] = request.form[MESSAGE_NAME]

  n_posts = count_posts()
  insert_post(params)
  assert count_posts() == n_posts + 1

  flash(CREATED_POST_TEXT)

  return redirect(url_for(INDEX_POSTS_ACTION) + '/' + params[TITLE_KEY])


@app.route(EDIT_POST_ROUTE)
def edit_post(resource):
  if not is_signed_in(): return redirect(LOGIN_ROUTE)

  db     = get_db()
  cursor = db.execute(FETCH_POST_SQL , [resource])
  result = cursor.fetchone()

  if result == None: abort(NOTFOUND_STATUS)

  update_url = INDEX_POSTS_ROUTE + result[TITLE_KEY] + UPDATE_PATH
  return render_template(EDIT_POST_HTML , post = result , header_text = EDIT_POST_TEXT.decode())


@app.route(UPDATE_POST_ROUTE , methods = [POST_METHOD])
def update_post(resource):
  if not is_signed_in(): abort(UNAUTH_STATUS)

  id      = request.form[DBID_NAME   ]
  title   = request.form[TITLE_NAME  ]
  message = request.form[MESSAGE_NAME]

  db = get_db()
  db.execute(UPDATE_POST_SQL , [ title , message , id ])
  db.commit()

  flash(UPDATED_POST_TEXT)

  return redirect(url_for(INDEX_POSTS_ACTION) + '/' + title)


@app.route(DELETE_POST_ROUTE , methods = [DELETE_METHOD])
def delete_post(resource):
  if not is_signed_in(): abort(UNAUTH_STATUS)

  n_posts = count_posts()
  db      = get_db()
  db.execute(DELETE_POST_SQL , [ request.form[DBID_NAME] ])
  db.commit()
  assert count_posts() == n_posts - 1

  flash(DELETED_POST_TEXT)

  return redirect(url_for(INDEX_POSTS_ACTION))
